/**
 * 
 */
package mysql;
/**
*@author tonychao 
*创建时间 ：2019年4月9日 下午7:32:16
*功能
*/

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.security.PrivilegedAction;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Map;

import javax.jws.soap.SOAPBinding.Use;
import javax.management.modelmbean.ModelMBean;

import org.json.JSONException;

import confs.SingleParameter;
import fetcher.APIFetcher;
import fetcher.RunRecord;
import graybox.CallPythonWindows;
import others.Global;
import others.Global.SparkTuneOption;
import shellInterface.UserSubmitInterface_test;

/**
 * @author asus
 *
 */
public class DataFile {
	private String filePath;
	private String modleDirPath;
	private String programMD5;
	private String className;
	private String statisticsPath;
	private double E_err, std_err,prediction_time_in_ns;
	public int heuristic_time_in_ms=-1;
	private String heuristic_time_record_path;
	//private String appid;
	public DataFile(String programMD5,String className) throws Exception{ //Application_name_md5_class.data
		this.className=className;
		this.programMD5=programMD5;
		String fileName="Application_Record_"+ programMD5 +"_"+ className+ ".data";
		this.filePath = Global.DATA_FILE_ROOT_PATH+fileName;
		this.modleDirPath=Global.MODEL_PATH+"Application_Model_"+programMD5+"_"+className+File.separator;
		this.statisticsPath=this.modleDirPath+"Model.statistics";
		this.heuristic_time_record_path=Global.DATA_FILE_ROOT_PATH+"hurstic_time_record"+className;
		if (this.className==null|| this.programMD5==null) {
			UserSubmitInterface_test.UIErr("无效的APPCLASS/APPID");
			UserSubmitInterface_test.UIErr(this.className+ "\t"+ this.programMD5);
			throw new Exception();
		}
	}
	
	public boolean isRegisted(String programMD5,String className){
		File file= new File(this.filePath);
		return file.exists();
	}
	
	public boolean hasModel(String programMD5,String className){
		File file = new File(this.modleDirPath);
		boolean ret= file.exists()&&file.isDirectory();
		return ret;
	}
	
	public boolean hasStatisticFileandHeuristicFile(){
		File file1 = new File(this.heuristic_time_record_path);
		File file2= new File(this.statisticsPath);
		return file1.exists()&&file2.exists();
	}
	
	//创建一个新的记录文件，因为在创建文件的时候还没有拿到数据记录，所以仅仅新建一个文件就结束了
	public String registe(String programMD5,String className) {
		File file = new File(this.filePath);
		if (file.exists()) System.err.println("文件已经存在 data File !"+this.filePath);
		try {
			file.createNewFile();
			//中间文件格式要求
	        if(!file.exists()) {
	        	UserSubmitInterface_test.UIErr("应该存在的调优文件不存在！"+filePath);
	        }
	        FileOutputStream out=new FileOutputStream(file,true); //追加
	        out.write(("3\n").getBytes("utf-8"));
	        out.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("创建新文件失败！ "+this.filePath);
			
		}
		return this.filePath;
	}
	
	//估计一下大概执行时间能够提升多少
	public double getassRatio(String programMD5,String className) throws IOException {
		ArrayList<Integer>timeList= new ArrayList<Integer>();
		//File file=new File(this.filePath);
		BufferedReader reader;
    	String preline="",line="";
		try {
			reader = new BufferedReader(new FileReader(this.filePath));
			while ((line=reader.readLine())!=null){
				if (line.trim().startsWith("//")) continue; //注释
				if (preline.trim().equals("#")){ //下一行是运行时间！
					String tmp=line.split(" ")[1];
					timeList.add(Integer.valueOf(tmp));
				}
			preline=line;
			}
			reader.close();
		} catch (FileNotFoundException e) {
			System.err.println("file not found");
			e.printStackTrace();
		}
		
		//如果暂时还没有历史记录的话,就先不估计
		if (timeList.size()==0) return 0;
		long sum=0;
		int min = Integer.MAX_VALUE;
		for (int i:timeList){
			sum += (long)i;
			if (min>i) min=i;
		}
		
		double ret= 1-min/(double)(sum/timeList.size());
		return ret;
	}
	
	
	//向一个已经存在的文件追加记录
	public void addRunRecord(String programMD5,String className,String app_id,int sleepTimeInMs,int inputFileSize) throws InterruptedException, JSONException, SQLException, ParseException, IOException {

		if (Global.RECORD_LOG_INTO_HISTORY==false) {
			UserSubmitInterface_test.UIOutPut("不记录应用历史记录");
			return;
		}
		//FileOutputStream out =new FileOutputStream(this.filePath,true); //追加模式
		UserSubmitInterface_test.UIOutPut("等待"+sleepTimeInMs+"ms....");
		Thread.sleep(sleepTimeInMs);
		RunRecord record =new APIFetcher().readRunRecord(app_id);
		writeRecordIntoDataFile(record, inputFileSize);
		//DBOutPut("添加新的运行记录");
	}


	private boolean writeRecordIntoDataFile(RunRecord runRecord,int inputFileSize) throws NumberFormatException, IOException, InterruptedException {
		//将参数压缩成字符串
		ArrayList<Integer> confListInInt1=new ArrayList<Integer>();
		ArrayList<Integer> confListInInt2=new ArrayList<Integer>();
		ArrayList<Integer> confListInInt3=new ArrayList<Integer>();

		Map<String, SingleParameter> parameterMap =runRecord.conf.parameterMap;
				
		//搜索每一组的参数并将其转换为int型，注意，其顺序由GlobalStatics 中的次序给次给定，且必须被严格遵守
		SingleParameter tmpParameter=null;
		
		//这是加上所有参数的
		for (String tmp: Global.CHOSEN_PARAMETERS_GROUP0){
			//if (tmp.equals("dummy")) continue;
			if(parameterMap.containsKey(tmp)){
				tmpParameter=parameterMap.get(tmp);
				confListInInt1.add(tmpParameter.value);
			}
		}
		
		for (String tmp: Global.CHOSEN_PARAMETERS_GROUP1){
			//if (tmp.equals("dummy")) continue;
			if(parameterMap.containsKey(tmp)){
				tmpParameter=parameterMap.get(tmp);
				confListInInt1.add(tmpParameter.value);
			}
		}
				
		for (String tmp: Global.CHOSEN_PARAMETERS_GROUP2){
			if (tmp.equals("dummy")){
				confListInInt2.add(-1);
				continue;
			}
			if(parameterMap.containsKey(tmp)){
				tmpParameter=parameterMap.get(tmp);
				confListInInt2.add(tmpParameter.value);
			}
		}
				
		for (String tmp: Global.CHOSEN_PARAMETERS_GROUP3){
			if (tmp.equals("dummy")){
				confListInInt3.add(-1);
				continue;
			}
			if(parameterMap.containsKey(tmp)){
				tmpParameter=parameterMap.get(tmp);
				confListInInt3.add(tmpParameter.value);
			}
		}
				
		long group1_time_ms=-1;
		long group2_time_ms=-1;
		long group3_time_ms=-1;
		long total_time_ms=-1;
				
		//目前暂时先使用总时间
		group1_time_ms =runRecord.getApplicationEndTime()-runRecord.getApplicationStartTime();
		total_time_ms=group1_time_ms;
		
		String app_id=runRecord.getApplicationID();
		
		File file=new File(this.filePath);
        if(!file.exists()) {
        	UserSubmitInterface_test.UIErr("应该存在的调优文件不存在！"+filePath);
        }
        FileOutputStream out=new FileOutputStream(file,true); //追加
        out.write(("#\n").getBytes("utf-8"));
        out.write((app_id+" "+String.valueOf(total_time_ms)+"\n").getBytes("utf-8")); //app_id total_time
		if(!(inputFileSize==-1)) {
			out.write((String.valueOf(group1_time_ms)+" "+drunkProgrammer(confListInInt1)+" "+String.valueOf(inputFileSize)+"\n").getBytes("utf-8"));
		}
		else {
			 out.write((String.valueOf(group1_time_ms)+" "+drunkProgrammer(confListInInt1)+"\n").getBytes("utf-8"));		// gropup1
		}
       									
        out.write((String.valueOf(group2_time_ms)+" "+drunkProgrammer(confListInInt2)+"\n").getBytes("utf-8"));											// gropup2
        out.write((String.valueOf(group3_time_ms)+" "+drunkProgrammer(confListInInt3)+"\n").getBytes("utf-8"));
        
        // gropup3

        out.write(("\n").getBytes("utf-8"));
        out.close();
        
        
        if(Global.DEFAULT_OPTION==SparkTuneOption.HURISTIC_ONLY){
        	//记录下来启发式参数调优方法的时间开销以备选择
        	File file2=new File(this.heuristic_time_record_path);
        	FileOutputStream out2 = new FileOutputStream(file2,true);
        	out2.write((String.valueOf(total_time_ms)+"\n").getBytes("utf-8"));
        	out2.close();
        }

		//增加application表  succeed_count 的计数,这里的逻辑和使用数据库的版本有较大差异，这里并不维护程序运行次数的计数,逻辑上更简略
		int count=get_succeed_count(this.programMD5,this.className);
		//检查之，如果计数 达到一定数量则调用训练脚本
		if (count%Global.MIN_FACTOR==0&&count>=Global.RETRAIN_COUNT){
			System.err.println("#############################################################################################");
			UserSubmitInterface_test.UIOutPut("未使用的模型数量已经达标，重新训练:  "+count);
			int ret =callTrainModel(new File(this.filePath).getAbsolutePath());
			if (ret==0){//建模成功
				UserSubmitInterface_test.UIOutPut("使用数据文件建模成功");
			}
			else {//建模失败
				System.err.println("模型建立失败！");
			}			
			
			System.err.println("#############################################################################################");
		}
		return false;
	}
	
	
	private String drunkProgrammer(ArrayList<Integer> list){
		String ret="";
		for (int i : list){
			ret=ret+String.valueOf(i)+" ";
		}
		ret.substring(0, ret.length()-1); //去掉最后多余的空格，python文件由于开发时间的原因健壮性不够好：(
		return ret;
	}

	public int get_succeed_count(String programMD5, String className) throws NumberFormatException, IOException {
		ArrayList<Integer>timeList= new ArrayList<Integer>();
		//File file=new File(this.filePath);
		BufferedReader reader;
    	String preline="",line="";
		try {
			reader = new BufferedReader(new FileReader(this.filePath));
			while ((line=reader.readLine())!=null){
				if (line.trim().startsWith("//")) continue; //注释
				if (preline.trim().equals("#")){ //下一行是运行时间！
					String tmp=line.split(" ")[1];
					timeList.add(Integer.valueOf(tmp));
				}
			preline=line;
			}
			reader.close();
		} catch (FileNotFoundException e) {
			System.err.println("file not found");
			e.printStackTrace();
		}
		return timeList.size();
	}
	
	
	/**
	 * 调用模块训练模型
	 * 此函数调用Python脚本重新训练模型，并且直接覆盖原有模型
	 * @throws InterruptedException 
	 * @throws IOException 
	 */
	public int callTrainModel(String dataFilePath) throws  IOException, InterruptedException{
		//开出去一个线程，然后就啥也不管了 也是美滋滋 然而我们的输出,以及waitfor也使得这个函数是阻塞式的
		CallPythonWindows tmp = new CallPythonWindows();
		//int ret=tmp.pythonRun(GlobalStatics.PYTHON_MAKE_MODEL_SCRIPT_DATAFILE,new String[]{MD5,className});
		int ret=tmp.pythonRun(Global.PYTHON_MAKE_MODEL_SCRIPT_DATAFILE,new String[]{dataFilePath});
		return ret;
	}
	
	// 从infor文件中读取信息
	private void readInforFromStatisticsFile() throws IOException{
		BufferedReader reader;
		reader = new BufferedReader(new FileReader(this.statisticsPath));
		String line=reader.readLine();
		String[] linelist=line.trim().split("\t");
		this.E_err= Double.valueOf(linelist[0]);
		this.std_err=Double.valueOf(linelist[1]);
		this.prediction_time_in_ns=Double.valueOf(linelist[2]);
		reader.close();
		reader = new BufferedReader(new FileReader(this.heuristic_time_record_path));
		int count =0,sum=0;
		while ((line=reader.readLine())!=null){
			count++;
			sum+=Integer.valueOf(line.trim());
			}
		this.heuristic_time_in_ms=sum/count;
		reader.close();	
	}
	
	public double get_E_err() throws IOException{
		if (this.heuristic_time_in_ms==-1){
			readInforFromStatisticsFile();
		}
		return this.E_err;

	}
	public double get_E_STD() throws IOException{
		if (this.heuristic_time_in_ms==-1){
			readInforFromStatisticsFile();
		}
		return this.std_err;
	}
	
	public double get_predictTime_in_ns() throws IOException{
		if (this.heuristic_time_in_ms==-1){
			readInforFromStatisticsFile();
		}
		return this.prediction_time_in_ns;
	}
	
	public double get_huristic_time_in_ms() throws IOException{
		if (this.heuristic_time_in_ms==-1){
			readInforFromStatisticsFile();
		}
		return this.heuristic_time_in_ms;
	}
}
